//
// Created by liuzelin3 on 2023/5/24.
//
#include "panda_memory_pool.hpp"
#include <stdexcept>
#include <algorithm>
using namespace Panda;

DataPool::DataList::DataList(size_t size){
    buff = new uint8_t [size];
    start = buff;
    end = buff + size;
}

DataPool::DataList::~DataList(){
    for(auto & p : ptrs) {
        if (p.second)
            throw std::runtime_error("Delete DataList Ptr Not Free");
    }
    delete[] buff;
}

DataPool::DataNode* DataPool::DataList::malloc(size_t size){
    DataNode* ret = nullptr;
    if (ptrs.empty()) {
        ret = (DataNode*)start;
        void* ptr = (uint8_t*)ret + size + sizeof(DataNode);
        if (ptr > end) {
            return nullptr;
        }
        ret->list = this;
        ptrs.emplace(std::make_pair(ret, true));
        ptrs.emplace(std::make_pair(ptr, false));
        return  ret;
    } else {
        for (auto& it : ptrs) {
            if (it.second == false) {
                auto next_it = next(ptrs.find(it.first));
                void* ptr = (uint8_t*)it.first + size + sizeof(DataNode);
                if (next_it != ptrs.end()) {
                    DataNode* ret = nullptr;
                    if (ptr < next_it->first) {
                        it.second = true;
                        ret = (DataNode*)it.first;
                        ptrs.emplace(std::make_pair(ptr, false));
                        ret->list = this;
                        return ret;
                    } else if (ptr == next_it->first) {
                        it.second = true;
                        ret = (DataNode*)it.first;
                        ret->list = this;
                        return ret;
                    } else {
                        continue;
                    }
                } else if (ptr <= end){
                    it.second = true;
                    ret = (DataNode*)it.first;
                    ptrs.emplace(std::make_pair(ptr, false));
                    ret->list = this;
                    return ret;
                }
            }
        }
    }
    return nullptr;
}

void DataPool::DataList::free(DataPool::DataNode* node){
    if (node == nullptr)
        return;
    auto it = ptrs.find(node);
    if (it == ptrs.end())
        return;
    it->second = false;
    auto prev_it = std::prev(it);
    void* next_ptr = nullptr;
    auto next_it = std::next(it);
    if (next_it != ptrs.end()) {
        next_ptr = next_it->first;
    }
    while (prev_it != ptrs.end() && prev_it->second == false) {  //前面有数据无效，删除自己
        ptrs.erase(it++);
        if (it == ptrs.begin())
            break;
        it = std::prev(it);
        if (it == ptrs.begin())
            break;
        prev_it = std::prev(it);
    }
    if (next_ptr) {
        next_it = ptrs.find( next_ptr);
        while (next_it != ptrs.end() && next_it->second == false) {  //后面有数据，删除后面
            ptrs.erase(next_it++);
        }
    }
}

void* DataPool::malloc (size_t size){
    if (size + sizeof (DataNode) > this->list_size)
    {
        list_size = size + sizeof (DataNode);
        list_size *= 5;
    }
    for (auto& list : lists) {
        auto ret = list->malloc(size);
        if (ret) {
            ret->pool = this;
            return ret->data;
        }
    }
    lists.emplace_back(new DataList(list_size));
    auto ret = lists.back()->malloc(size);
    if (ret) {
        ret->pool = this;
        return ret->data;
    }
    throw std::runtime_error("DataPool Malloc Error");
}

void DataPool::free(void*ptr){
    DataNode* node = (DataNode*)((uint8_t*)ptr - sizeof (DataNode));
    if (node->pool != this)
        throw std::runtime_error("DataPool Free Pool Handler Error");
    DataList* list = (DataList*)node->list;
    auto it = std::find_if(lists.begin(),lists.end(),[list](DataList* l) {return l == list;});
    if (it == lists.end())
        throw std::runtime_error("DataPool Free list Handler Error");
    (*it)->free(node);
}

DataPool::~DataPool(){
    for (auto& l : lists) {
        delete l;
    }
    lists.clear();
}